// UserModeThread.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>

__declspec(naked) void switch_context(void**& from,void** to)
{
	_asm
	{
		// save context
		push ebp
		mov ebp,esp
		push ebx
		push esi
		push edi

		// switch stack.
		mov eax,[from]
		mov [eax],esp
		mov esp,to

		// restore context
		pop edi
		pop esi
		pop ebx
		pop ebp

		// jump to function
		pop eax
		jmp eax
	}
}

class coroutine
{
	typedef void (*FunctionType)(coroutine& self);
	const static size_t stack_size = 4096;

	bool exited;
	char* stack;

	class routine
	{
		FunctionType	func;
		coroutine&		corou;
	public:
		routine(FunctionType f,coroutine& co) : func(f),corou(co)
		{}

		void operator()()
		{
			func(corou);
			corou.exited = true;
			corou.yield();
		}
	};

	routine rout;
	void**	stackptr;
	void**	savedsp;

	static void call_routine(routine& r)
	{
		r();
	}

public:
	coroutine(FunctionType f) : exited(false),stack(new char[stack_size]),rout(f,*this)
	{
		stackptr = (void**)(stack + stack_size);

		*(--stackptr) = &rout;			// parameter 1
		*(--stackptr) = 0;				// dummy return address for call_routine 
		*(--stackptr) = (void*)call_routine;	// return address
		*(--stackptr) = 0;				// ebp
		*(--stackptr) = 0;				// ebx
		*(--stackptr) = 0;				// esi
		*(--stackptr) = 0;				// edi

	}
	~coroutine()
	{
		/*
			delete stack; // comment by Lucifer
		*/

		 delete [] stack;
		 stack	= (char*)0;
	}

	void operator()()
	{
		if (!exited)
		{
			//switch to routine.
			switch_context(savedsp,stackptr);
		}
	}
	operator bool () const
	{
		return !exited;
	}
	void yield()
	{
		//switch back.
		switch_context(stackptr,savedsp);
	}
};

void f1(coroutine& self)
{
	for (int i=0;i<10;i++)
	{
		printf("%d\n",i);
		self.yield();
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	coroutine routine(f1);

	while (routine)
	{
		printf("main thread\n");
		routine();
	}
	return 0;
}

